package com.nce.converter.audio;

import java.io.IOException;
import java.util.Vector;

import javax.media.Buffer;
import javax.media.Codec;
import javax.media.Format;
import javax.media.ResourceUnavailableException;
import javax.media.format.AudioFormat;

import com.nce.converter.TranscoderOutput;
import com.nce.protocol.rtp.ParticipantRTPStream;


public class CodecWrapper implements TranscoderOutput {
	Codec codec = null;
	Format format = null;
	AudioFormat outputFormat = null;
	Object syncher = null;
	
	ParticipantRTPStream stream = null;
	Vector<Buffer> queueOut = null;
	
	private long lastTimeStampSeen = -1;
	private int lastPacketSize = -1;
	private long offset = -1;
	
	boolean stopped = false;
	
	public CodecWrapper(Format input, ParticipantRTPStream stream, long offset)
	{
		syncher = new Object();
		this.format = input;
		this.offset = offset;
		this.stream = stream;
		int channels = 1;
		int samplerate = 8000;
		int samplesizeinbits = 16;
		
		outputFormat = new AudioFormat(AudioFormat.LINEAR, samplerate, samplesizeinbits, channels);
		codec = getCodecForFormat(format);
		if (codec != null)
		{
			codec.setInputFormat(this.format);
			codec.setOutputFormat(outputFormat);
		}
		queueOut = new Vector<Buffer>();
	}
	
	
	private static Codec getCodecForFormat(Format format)
	{
		String encoding = format.getEncoding();
		Codec codec = null;
		String name = null;
		if (encoding.equals(AudioFormat.ULAW) || encoding.equals(AudioFormat.ULAW_RTP))
		{
			name = new String("com.ibm.media.codec.audio.ulaw.JavaDecoder");
		}
		else if (encoding.equals(AudioFormat.ALAW))
		{
			name = new String("com.ibm.media.codec.audio.alaw.JavaDecoder");
		}
		else if (encoding.equals(AudioFormat.GSM) || encoding.equals(AudioFormat.GSM_RTP))
		{
			name = new String("com.ibm.media.codec.audio.gsm.JavaDecoder");
		}
		else if (encoding.equals(AudioFormat.G723) || encoding.equals(AudioFormat.G723_RTP))
		{
			name = new String("com.ibm.media.codec.audio.g723.JavaDecoder");
		}
		try {
			if (name != null)
			{
				Class<?> c = Class.forName("com.ibm.media.codec.audio.ulaw.JavaDecoder");
				Object o = c.newInstance();
				if (o instanceof Codec)
				{
					codec = (Codec)o;
					codec.open();
				}
			}
		}
		catch (ClassNotFoundException ex)
		{
			System.err.println("++++getCodecForFormat() : couldn't find the codec \n" + ex);
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ResourceUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return codec;
	}
	
	private Buffer createSilenceBuffer(Format f, long duration)
	{
		Buffer silence = new Buffer();
		byte [] silenceData = new byte[(int)duration];
		for (int i = 0; i < silenceData.length; i++)
			silenceData[i] = 0;
		silence.setData(silenceData);
		silence.setLength(silenceData.length);
		silence.setOffset(0);
		silence.setFlags(Buffer.FLAG_SILENCE);
		silence.setFormat(f);
		return silence;
	}
	
	
	public boolean decodePacket()
	{
		Buffer outBuff = new Buffer();
		Buffer inBuff = new Buffer();
		Buffer inSilence = null;
		Buffer outSilence = null;
		try {
			if (stream.endOfStream())
				return false;
			stream.read(inBuff);
		}
		catch (IOException e)
		{
			System.err.println("+++++++decodePacket() : ioexception \n" + e);
			return false;
		}
		boolean noProblem = false;
		if (inBuff.getData()!=null);
		{
			if (lastTimeStampSeen == -1)
			{
				lastTimeStampSeen=inBuff.getTimeStamp();
			}
			
			long deltaT = inBuff.getTimeStamp() - lastTimeStampSeen;
			
			if (deltaT > lastPacketSize)
			{
				inSilence = createSilenceBuffer(inBuff.getFormat(), deltaT);
				outSilence = new Buffer();
				codec.process(inSilence, outSilence);
			}
			
			noProblem =	codec.process(inBuff, outBuff)  == Codec.BUFFER_PROCESSED_OK;
			lastTimeStampSeen = inBuff.getTimeStamp();
			lastPacketSize = inBuff.getLength();
		}
		if (noProblem)
		{
			
			
			synchronized(syncher)
			{
				if (queueOut.size() == 0)
				{
					syncher.notify();
				}
				if (outSilence!= null)
					queueOut.add(outSilence);
				queueOut.add(outBuff);
				
			}
		}
			
		if (noProblem)
			return true;
		else return false;
	}
	
	public Buffer getNextOutput()
	{
		synchronized(syncher)
		{
			try {
				while (queueOut.isEmpty() && !stopped)
				{
					syncher.wait();
				}
			}
			catch (InterruptedException e)
			{
				System.err.println("getNextOutput.... interrupted exception \n" + e);
			}
			if (!queueOut.isEmpty() && !stopped)
			{
				return queueOut.remove(0);
			}
			else return null;
		}
	}
	
	public boolean willBlock()
	{
		synchronized(syncher)
		{
			return (queueOut.isEmpty() && !stopped);
		}
	}
	
	public boolean isStillOutputting()
	{
		return !stopped;
	}
	
	public long getOffset()
	{
		return offset;
	}
	public void stop()
	{
		synchronized(syncher)
		{
			stopped = true;
			syncher.notify();
		}
	}
	
}
